问题的起因是在阅读java8的源码中发现BaseStream
的申明
public interface BaseStream<T, S extends BaseStream<T, S>> {...}
其中S extends BaseStream<T, S>
相当隐晦,不知道啥深意。Google之后豁然开朗,又涨姿势。
要理解上面这段代码可以先从简单的情景出发,先要理解它想达到什么效果。比如说我们有下面的两个类:
public class Foo {
Foo doSomething() {
// ...
return this;
}
}
public class Bar extends Foo {
Bar doOtherthing() {
// ...
return this;
}
}
如果想实现链式调用,new Bar().doSomething().doOtherthing()
会报编译错误,因为doSomething
返回的是Foo对象而不含有doOtherthing
这个成员函数。解决方法是继承重写:
public class Bar extends Foo {
Bar doOtherthing() {
// ...
return this;
}
@Override
Bar doSomething() {
super.doSomething();
return this;
}
}
然而,如果Foo有很多个链式函数,那Bar也必须全部重写,这显然不好。可以改写下Foo,让doSomething
返回根据模板参数确定的类型T,然后Bar继承FoodoSomething
知道它应该返回什么类型。
public class Foo<T> {
Foo doSomething() {
// ...
return (T) this;
}
}
public class Bar extends Foo<Bar> {
Bar doOtherthing() {
// ...
return this;
}
}
但是这么做没有检查T的类型,T应该必须是Foo的子类,而不能是任意的类型。可以给它加上类型限制:
public class Foo<T extends Foo<T>> {
Foo doSomething() {
// ...
return (T) this;
}
}
注意到T extends Foo<T>
和 Bar extends Foo<Bar>
形式是一致的,所以Bar满足了要求。
这个例子其实就是BaseStream
中S extends BaseStream<T, S>
的简化(去除了T类型)。总结来说
那段代码是为了约束S必须是BaseStream
的子类。
这种模式很早就有人发现了,称为Curiously recurring template pattern 除了在链式调用中的应用外,还有其他有意思的应用,这里就不展开说了。